package com.google.code.guava.reflect.predicate;

import java.util.Arrays;

import com.google.code.guava.base.BasePredicate;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;

public abstract class QualifiersPredicate<T> extends BasePredicate<T>{

    private final QualifierEvalutionStrategy strategy;

    private final Qualifier[] modifiers;

    private final QualifiedType holderType;

    QualifiersPredicate(final QualifierEvalutionStrategy strategy,
        final Qualifier[] modifiers, final QualifiedType holderType){
        Preconditions.checkNotNull(strategy);
        Preconditions.checkNotNull(modifiers);
        Preconditions.checkNotNull(holderType);
        this.holderType = holderType;
        for(final Qualifier qualifier : modifiers){
            Preconditions.checkArgument(qualifier.supportedTypes()
                .contains(holderType),
                "Invalid modifier %s for type %s",
                qualifier,
                holderType);
        }
        this.strategy = strategy;
        this.modifiers = modifiers;
    }

    public final boolean apply(final T member){
        return this.strategy.evaluate(Arrays.asList(this.modifiers),
            this.getModifierMask(member));
    }

    protected abstract int getModifierMask(T object);

    @Override
    public String toString(){
        return Objects.toStringHelper(this)
        .add("target type", holderType)
        .add("modifiers", Arrays.toString(this.modifiers))
        .add("Strategy", this.strategy)
        .toString();
    }

    @Override
    protected boolean equalsPredicate(final Predicate<?> other){
        if(other instanceof QualifiersPredicate<?>){
            final QualifiersPredicate<?> otherMp = (QualifiersPredicate<?>) other;
            return Objects.equal(holderType, otherMp.holderType)
            && Objects.equal(strategy, otherMp.strategy)
            && Iterables.elementsEqual(Arrays.asList(modifiers),
                Arrays.asList(otherMp.modifiers));
        }
        return false;
    }

    @Override
    public int hashCode(){
        return Objects.hashCode(holderType, strategy, modifiers);
    }

}
